﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EPiServer;
using EPiServer.Core;
using EPiServer.ServiceLocation;

namespace EPiBoost.Core.Extensions
{
	public static class PageDataTraversalExtensions
	{ 

		#region Children

		public static PageDataCollection GetChildren(this PageData page)
		{
			var children =  ServiceLocator.Current.GetInstance<IContentRepository>().GetChildren<PageData>(page.PageLink);
			return new PageDataCollection(children);
			//return DataFactory.Instance.GetChildren(page.PageLink);
		}

		public static IEnumerable<T> GetChildren<T>(this PageData page) where T : PageData
		{
			return ServiceLocator.Current.GetInstance<IContentRepository>().GetChildren<T>(page.PageLink);
			//return DataFactory.Instance.GetChildren<T>(page.PageLink);
		}


		#endregion 

		#region Descendants

		public static PageDataCollection GetAllDescendants(this PageData page)
		{
			//var descendants = DataFactory.Instance.GetDescendents(page.PageLink);
			var descendants = ServiceLocator.Current.GetInstance<IContentRepository>().GetDescendents(page.PageLink);
			var result = new PageDataCollection();
			foreach (var descendant in descendants)
			{
				var pageRef = descendant.ToPageReference();
				if(!PageReference.IsNullOrEmpty(pageRef)){
					result.Add(pageRef.GetPage());
				}
			}
			return result;
		}

		#endregion


		#region Parents

		/// <summary>
		/// Gets the parent page data of the current page
		/// </summary>
		/// <param name="rootPage"></param>
		/// <returns></returns>
		public static PageData GetParentPage(this PageData rootPage)
		{
			if (rootPage != null && !PageReference.IsNullOrEmpty(rootPage.ParentLink))
			{
				return rootPage.ParentLink.GetPage();
			}
			return null;
		}



		/// <summary>
		/// Gets all parents from the current page up to the root
		/// </summary>
		/// <param name="rootPage"></param>
		/// <param name="stopPageReference"> </param>
		/// <returns></returns> 
		public static PageDataCollection GetAllParents(this PageData rootPage, PageReference stopPageReference = null)
		{
			var parents = new PageDataCollection();
			PageData page = rootPage;

			PageReference stopReference = PageReference.IsNullOrEmpty(stopPageReference)
											? PageReference.StartPage
											: stopPageReference;

			while (page != null && page.PageLink != stopReference && !PageReference.IsNullOrEmpty(page.ParentLink))
			{
				page = page.ParentLink.GetPage();
				parents.Add(page);
			}
			return parents;
		}


		/// <summary>
		/// Recursively looks for parent pages with matching PageTypName
		/// </summary>
		public static T FindFirstParentOfType<T>(this PageData rootPage, bool includeRootPage = true) where T : PageData
		{
			var page = rootPage;
			if (!includeRootPage)
			{
				page = page.GetParentPage();
			}
			while (page != null)
			{
				if (page is T)
				{
					return page.PageLink.GetPage<T>();
				}
				page = page.GetParentPage();
			}
			return null;
		}

		/// <summary>
		/// Recursively looks for parent pages which implement the specified interface
		/// </summary>
		public static T FindFirstParentImplementingInterface<T>(this PageData rootPage, bool includeRootPage = true)
		{
			var page = rootPage;
			if (!includeRootPage)
			{
				page = page.GetParentPage();
			}
			while (page != null)
			{
				if (page.ImplementsInterface(typeof(T)))
				{
					return (T)(object)page;
				}
				page = page.GetParentPage();
			}

			return default(T);
		}


		/// <summary>
		/// Recursively looks for parent pages which implement the specified interface
		/// </summary>
		public static PageData FindFirstParentImplementingInterface(this PageData rootPage, Type interfaceType, bool includeRootPage = true)
		{
			var page = rootPage;
			if (!includeRootPage)
			{
				page = page.GetParentPage();
			}
			while (page != null)
			{
				if (page.ImplementsInterface(interfaceType))
				{
					return page;
				}
				page = page.GetParentPage();
			}

			return null;
		}

		#endregion

		#region Siblings

		/// <summary>
		/// Gets all pages at the same level as current page
		/// </summary>
		/// <param name="rootPage"></param>
		/// <param name="excludeSourcePage"> </param>
		/// <returns></returns>
		public static PageDataCollection GetAllSiblings(this PageData rootPage, bool excludeSourcePage = true)
		{
			if (rootPage.ParentLink == PageReference.RootPage)
			{
				return new PageDataCollection { rootPage };
			}
			var siblings = rootPage.GetParentPage().GetChildren();
			if (excludeSourcePage)
			{
				siblings = new PageDataCollection((from s in siblings where s.PageLink != rootPage.PageLink select s));
			}
			return siblings;
		}

		public static PageDataCollection GetSiblings<T>(this PageData rootPage, bool excludeSourcePage = true)
		{
			return (PageDataCollection)GetAllSiblings(rootPage, excludeSourcePage).OfType<T>();
		}

		#endregion
	}
}
